package cn.tedu.medicalappointmentsystemk.service.impl;

import cn.tedu.medicalappointmentsystemk.dao.cache.UserCacheRepository;
import cn.tedu.medicalappointmentsystemk.dao.persist.repository.PatientRepository;
import cn.tedu.medicalappointmentsystemk.dao.persist.repository.UserRepository;
import cn.tedu.medicalappointmentsystemk.dao.persist.repository.UserRoleRepository;
import cn.tedu.medicalappointmentsystemk.handler.ServiceException;
import cn.tedu.medicalappointmentsystemk.pojo.authentication.CurrentPrincipal;
import cn.tedu.medicalappointmentsystemk.pojo.bo.UserBO;
import cn.tedu.medicalappointmentsystemk.pojo.bo.UserSelectByIdBO;
import cn.tedu.medicalappointmentsystemk.pojo.param.*;
import cn.tedu.medicalappointmentsystemk.pojo.po.*;
import cn.tedu.medicalappointmentsystemk.pojo.bo.UserSelectBO;
import cn.tedu.medicalappointmentsystemk.pojo.vo.UserLoginVO;
import cn.tedu.medicalappointmentsystemk.pojo.vo.UserSelectVO;
import cn.tedu.medicalappointmentsystemk.pojo.vo.UserUpdateVO;
import cn.tedu.medicalappointmentsystemk.response.ServiceCode;
import cn.tedu.medicalappointmentsystemk.service.UserService;
import cn.tedu.medicalappointmentsystemk.util.JwtUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Value("${medical.jwt.secret-key}")
    private String secretKey;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PatientRepository patientRepository;

    @Autowired
    private UserCacheRepository userCacheRepository;

    @Autowired
    private UserRoleRepository userRoleRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void reg(UserRegParam userRegParam) {
        log.debug("开始处理【用户注册】的业务,参数:{}",userRegParam);
        //检查用户名是否被占用
        {
            String username = userRegParam.getUsername();
            Integer count = userRepository.countByUsername(username);
            if (count > 0) {
                String message = "用户名已存在!";
                log.error(message);
                throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
            }
        }
        // 检查手机号码是否被占用
        {
            String mobile = userRegParam.getMobile();
            Integer count = userRepository.countByMobile(mobile);
            if (count > 0) {
                String message = "添加用户失败,手机号码已经被占用!";
                log.error(message);
                throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
            }
        }
        // 检查电子邮箱是否被占用
        {
            String email = userRegParam.getEmail();
            Integer count = userRepository.countByEmail(email);
            if (count > 0) {
                String message = "添加用户失败,电子邮箱已经被占用!";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
            }
        }
        UserPO po = new UserPO();
        BeanUtils.copyProperties(userRegParam,po);
        po.setEnable(ENABLE_STATE_ON);
        String rawPassword = po.getPassword();
        String encodedPassword = passwordEncoder.encode(rawPassword);
        po.setPassword(encodedPassword);
        Integer rows = userRepository.addNew(po);
        if (rows != 1){
            String message = "注册失败!请稍后再试!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_INSERT,message);
        }
        PatientPO patient = new PatientPO();
        patient.setUserId(po.getId());
        Integer row = patientRepository.addNew(patient);
        if (row != 1){
            String message = "注册失败!请稍后再试!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_INSERT,message);
        }
        Long roleId = userRegParam.getRoleId();
        UserRolePO userRole = new UserRolePO();
        userRole.setUserId(po.getId());
        userRole.setRoleId(roleId);
        rows = userRoleRepository.insert(userRole);
        if (rows < 1) {
            String message = "添加用户失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_INSERT, message);
        }
    }

    @Override
    public UserBO login(UserLoginParam userLoginParam,String remoteAddr,String userAgent) {
        log.debug("开始处理【用户登录】的业务，参数：{}", userLoginParam);
        String username = userLoginParam.getUsername();
        UserLoginVO vo=userRepository.selectByUsername(username);
        if(vo==null){
            String message = "登录失败,用户名不存在!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UNAUTHORIZED,message);
        }

        if (!vo.getEnable().equals(ENABLE_STATE_ON)) {
            String message = "登录失败，此账号已经被禁用！";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UNAUTHORIZED_DISABLED, message);
        }

        String password = userLoginParam.getPassword();
        if(!passwordEncoder.matches(password, vo.getPassword())){
            String message = "登录失败,密码错误!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UNAUTHORIZED,message);
        }

        Long userId = vo.getId();
        String imgUrl = vo.getImgUrl();
        String nickname = vo.getNickname();

        List<GrantedAuthority> authorities = new ArrayList<>();
        List<String> flags = vo.getFlags();
        for (String flag : flags) {
            authorities.add(new SimpleGrantedAuthority(flag));
        }
        String authoritiesJsonString = JSON.toJSONString(authorities);

        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_USER_ID, userId);
        claims.put(CLAIM_USER_NAME, username);
        claims.put(CLAIM_USER_AGENT, userAgent);
        claims.put(CLAIM_REMOTE_ADDR, remoteAddr);
        String jwt = JwtUtils.generate(claims, secretKey);
        log.debug("生成用户的JWT数据：{}", jwt);

        UserCachePO userCachePO = new UserCachePO();
        userCachePO.setEnable(ENABLE_STATE_ON);
        userCachePO.setAuthoritiesJsonString(authoritiesJsonString);
        userCacheRepository.saveUserState(userId, userCachePO);
        log.debug("向缓存中存入用户状态数据：{}", userCachePO);

        UserBO bo = new UserBO()
                .setId(userId)
                .setUsername(username)
                .setNickname(nickname)
                .setToken(jwt)
                .setImgUrl(imgUrl)
                .setAuthorities(flags);
        log.debug("即将返回用户的登录结果：{}", bo);
        return bo;
    }

    @Override
    public void changePassword(UserChangePasswordParam userChangePasswordParam) {
        UserPO userPO = userRepository.selectById(userChangePasswordParam.getId());
        if (!userPO.getPassword().equals(userChangePasswordParam.getOldPassword())){
            String message = "您输入的旧密码错误!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_INSERT,message);
        }
        userPO.setPassword(userChangePasswordParam.getNewPassword());
        Integer rows = userRepository.updatePassword(userPO);
        if (rows != 1){
            String message = "更新失败!请稍后再试!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UPDATE,message);
        }
    }

    @Override
    public void changePassword(UserForgetPasswordParam userForgetPasswordParam) {
        UserPO userPO = userRepository.selectByMobile(userForgetPasswordParam.getMobile());
        if (!userPO.getMobile().equals(userForgetPasswordParam.getMobile())){
            String message = "您输入的手机号错误!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_INSERT,message);
        }
        userPO.setPassword(userForgetPasswordParam.getPassword());
        Integer rows = userRepository.updatePassword(userPO);
        if (rows != 1){
            String message = "更新失败!请稍后再试!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UPDATE,message);
        }
    }

    @Override
    public UserSelectByIdBO selectUserById(Long id) {
        UserSelectVO vo = userRepository.selectUserById(id);
        UserSelectByIdBO bo = new UserSelectByIdBO();
        BeanUtils.copyProperties(vo,bo);
        return bo;
    }

    @Override
    public UserSelectBO selectById(Long id) {
        UserSelectVO vo = userRepository.selectUserById(id);
        UserSelectBO bo = new UserSelectBO();
        BeanUtils.copyProperties(vo,bo);
        bo.setNumber(patientRepository.selectDetailById(vo.getPatientId()).getNumber());
        return bo;
    }

    @Override
    public void logout(CurrentPrincipal currentPrincipal) {
        Long userId = currentPrincipal.getId();
        boolean deleteResult = userCacheRepository.deleteUserState(userId);
        if (!deleteResult) {
            String message = "操作失败，用户数据有误！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }
    }

    @Override
    public void changeUser(UpdateUserParam updateUserParam) {
        UserUpdateVO vo = new UserUpdateVO();
        BeanUtils.copyProperties(updateUserParam,vo);
        Integer rows = userRepository.updateUser(vo);
        if (rows == 0){
            String message = "修改失败!请稍后再试!";
            log.error(message);
            throw new ServiceException(ServiceCode.ERROR_UPDATE,message);
        }
    }

    @Override
    public List<UserSelectBO> select() {
        return userRepository.select();
    }
}
